package com.vekslers;

import java.util.Enumeration;
import java.util.Locale;
import java.util.regex.Pattern;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

/**
 * toString() generator for HttpServletRequest
 */
public final class ServletTool {
	@SuppressWarnings("unchecked")
	public static StringBuilder printHeaders(StringBuilder appendTo, HttpServletRequest servletRequest) {
		Enumeration<String> headerNames = servletRequest.getHeaderNames();

		if(headerNames != null) {
			while(headerNames.hasMoreElements()) {
				String headerName = headerNames.nextElement();
				
				appendTo.append(" ");
				Enumeration<String> headerValues = servletRequest.getHeaders(headerName);
				appendTo.append(headerName).append(" = ");
				while(headerValues.hasMoreElements()) {
					appendTo.append("[");
					appendTo.append(headerValues.nextElement());
					appendTo.append("]");
					if(headerValues.hasMoreElements()) {
						appendTo.append(", ");
					}
				}
				appendTo.append("\n");
			}
		}		
		
		return appendTo;
	}

	public static String printHeaders(HttpServletRequest servletRequest) {
		return printHeaders(new StringBuilder(), servletRequest).toString();
	}
	
	@SuppressWarnings("unchecked")
	public static StringBuilder printParameters(StringBuilder appendTo, HttpServletRequest servletRequest) {
		Enumeration<String> parameterNames = servletRequest.getParameterNames();

		if(parameterNames != null) {
			while(parameterNames.hasMoreElements()) {
				String parameterName = parameterNames.nextElement();
	
				appendTo.append(" ");
				String[] parameterValues = servletRequest.getParameterValues(parameterName);
				appendTo.append(parameterName).append(" = ");
				for(String parameterValue : parameterValues) {
					appendTo.append("[");
					appendTo.append(parameterValue);
					appendTo.append("]");
					appendTo.append(", ");
				}
				appendTo.deleteCharAt(appendTo.length()-2);
				appendTo.append("\n");
			}
		}
		
		return appendTo;
	}

	public static String printParameters(HttpServletRequest servletRequest) {
		return printParameters(new StringBuilder(), servletRequest).toString();
	}
	
	public static StringBuilder printCookies(StringBuilder appendTo, HttpServletRequest servletRequest) {
		Cookie[] cookies = servletRequest.getCookies();

		if(cookies != null) {
			for(int i = 0; i < cookies.length; i++) {
				Cookie cookie = cookies[i];
				appendTo.append(" ================================================================").append("\n");
				appendTo.append(" Comment = [").append(cookie.getComment()).append("]\n");
				appendTo.append(" Domain = [").append(cookie.getDomain()).append("]\n");
				appendTo.append(" MaxAge = [").append(cookie.getMaxAge()).append("]\n");
				appendTo.append(" Name = [").append(cookie.getName()).append("]\n");
				appendTo.append(" Path = [").append(cookie.getPath()).append("]\n");
				appendTo.append(" Secure = [").append(cookie.getSecure()).append("]\n");
				appendTo.append(" Value = [").append(cookie.getValue()).append("]\n");
				appendTo.append(" Version = [").append(cookie.getVersion()).append("]\n");
				appendTo.append(" ================================================================").append("\n");
				}
		}
		
		return appendTo;
	}

	public static String printCookies(HttpServletRequest servletRequest) {
		return printCookies(new StringBuilder(), servletRequest).toString();
	}

	public static StringBuilder printAccess(StringBuilder appendTo, HttpServletRequest servletRequest) {
		appendTo.append(" RequestURL = [").append(servletRequest.getRequestURL()).append("]\n");
		appendTo.append(" QueryString = [").append(servletRequest.getQueryString()).append("]\n");
		appendTo.append(" RemoteAddr = [").append(servletRequest.getRemoteAddr()).append("]\n");
		appendTo.append(" Method = [").append(servletRequest.getMethod()).append("]\n");
		appendTo.append(" Protocol = [").append(servletRequest.getProtocol()).append("]\n");
		appendTo.append(" RequestURI = [").append(servletRequest.getRequestURI()).append("]\n");
		// NOTE: Do not call servletRequest.getSession(), otherwise it will create an empty session! And we don't want that!
		
		return appendTo;
	}

	public static String printAccess(HttpServletRequest servletRequest) {
		return printAccess(new StringBuilder(), servletRequest).toString();
	}

	public static StringBuilder printMeta(StringBuilder appendTo, HttpServletRequest servletRequest) {
		appendTo.append(" Scheme = [").append(servletRequest.getScheme()).append("]\n");
		appendTo.append(" ServletPath = [").append(servletRequest.getServletPath()).append("]\n");
		appendTo.append(" PathTranslated = [").append(servletRequest.getPathTranslated()).append("]\n");
		appendTo.append(" RemoteUser = [").append(servletRequest.getRemoteUser()).append("]\n");
		appendTo.append(" RequestedSessionId = [").append(servletRequest.getRequestedSessionId()).append("]\n");
		appendTo.append(" RequestedSessionIdValid = [").append(servletRequest.isRequestedSessionIdValid()).append("]\n");
		appendTo.append(" RequestedSessionIdFromCookie = [").append(servletRequest.isRequestedSessionIdFromCookie()).append("]\n");
		appendTo.append(" RequestedSessionIdFromURL = [").append(servletRequest.isRequestedSessionIdFromURL()).append("]\n");
		appendTo.append(" CharacterEncoding = [").append(servletRequest.getCharacterEncoding()).append("]\n");
		appendTo.append(" ContentLength = [").append(servletRequest.getContentLength()).append("]\n");
		appendTo.append(" CharacterEncoding = [").append(servletRequest.getCharacterEncoding()).append("]\n");
		appendTo.append(" ContentType = [").append(servletRequest.getContentType()).append("]\n");
		appendTo.append(" ContextPath = [").append(servletRequest.getContextPath()).append("]\n");
		appendTo.append(" PathInfo = [").append(servletRequest.getPathInfo()).append("]\n");
		appendTo.append(" ServerName = [").append(servletRequest.getServerName()).append("]\n");
		appendTo.append(" ServerPort = [").append(servletRequest.getServerPort()).append("]\n");
		appendTo.append(" isSecure = [").append(servletRequest.isSecure()).append("]\n");
		appendTo.append(" LocalAddr = [").append(servletRequest.getLocalAddr()).append("]\n");
		appendTo.append(" LocalPort = [").append(servletRequest.getLocalPort()).append("]\n");
		appendTo.append(" RemotePort = [").append(servletRequest.getRemotePort()).append("]\n");

		return appendTo;
	}

	public static String printMeta(HttpServletRequest servletRequest) {
		return printMeta(new StringBuilder(), servletRequest).toString();
	}


	@SuppressWarnings("unchecked")
	public static StringBuilder printLocales(StringBuilder appendTo, HttpServletRequest servletRequest) {
		Enumeration<Locale> localNames = servletRequest.getLocales();

		if(localNames != null) {
			while(localNames.hasMoreElements()) {
				Locale locale = localNames.nextElement();
				appendTo.append(" ").append(locale.toString()).append("\n");
			}
		}		
		
		return appendTo;
	}

	public static String printLocales(HttpServletRequest servletRequest) {
		return printLocales(new StringBuilder(), servletRequest).toString();
	}

	public static String toStringHttpServletRequest(HttpServletRequest servletRequest) {
		return toStringHttpServletRequest(new StringBuilder(), servletRequest).toString();
	}
	
	/**
	 * Convert a servlet request object into string representation 
	 *  that can be used for log output
	 *  
	 * @param servletRequest
	 * @return
	 */
	public static StringBuilder toStringHttpServletRequest(StringBuilder appendTo, HttpServletRequest servletRequest) {
		appendTo.append("Access Info:\n");
		printAccess(appendTo, servletRequest);
		appendTo.append("\n\n");

		appendTo.append("Parameter Info:\n");
		printParameters(appendTo, servletRequest);
		appendTo.append("\n\n");
		
		appendTo.append("Header Info:\n");
		printHeaders(appendTo, servletRequest);
		appendTo.append("\n\n");

		appendTo.append("Meta Info:\n");
		printMeta(appendTo, servletRequest);
		appendTo.append("\n\n");

		appendTo.append("Cookie Info:\n");
		printCookies(appendTo, servletRequest);
		appendTo.append("\n\n");

		appendTo.append("Locale Info:\n");
		printLocales(appendTo, servletRequest);
		appendTo.append("\n\n");

		return appendTo;
	}
	
	/**
	 *  The X-Forwarded-For (XFF) HTTP header is a de facto standard for identifying the originating IP address of a client 
	 *  connecting to a web server through an HTTP proxy or load balancer.
	 *  
	 *  http://en.wikipedia.org/wiki/X-Forwarded-For
	 */
	private static final String X_Forwarded_For_PARAM = "X-Forwarded-For";
	
	// Regex utility to extract fields from X-Forwarded-For header. Several fields are seperated by ",".
	private static Pattern HEADER_SPLIT_TOOL = Pattern.compile(",");

	public static String clientIP(HttpServletRequest httpServletRequest) {
		String X_FORWARDED_FOR_HEADER = httpServletRequest.getHeader(X_Forwarded_For_PARAM);

		if(StringTool.isNotEmpty(X_FORWARDED_FOR_HEADER)) { // If we got X_FORWARDED_FOR_HEADER extract values from it.
			return HEADER_SPLIT_TOOL.split(X_FORWARDED_FOR_HEADER)[0];
		} else { // Else, if no X_FORWARDED_FOR_HEADER return plain client IP as reported by tcp stack.
			return httpServletRequest.getRemoteAddr();
		}

	}
}
